@file:Suppress("unused")

package com.sscl.easyble

import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import androidx.annotation.RequiresApi
import com.sscl.easyble.enums.LogLevel
import com.sscl.easyble.extensions.hasBluetoothConnectPermission
import com.sscl.easyble.interfaces.OnBluetoothStateChangedListener
import com.sscl.easyble.interfaces.OnDeviceBondStateChangedListener
import com.sscl.easyble.receiver.DeviceBoundBroadcastReceiver
import com.sscl.easyble.variable.GlobalConnectVariable
import com.sscl.easyble.variable.GlobalVariable

object BleManager {

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * 私有属性
     *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    /* * * * * * * * * * * * * * * * * * * 常量私有属性 * * * * * * * * * * * * * * * * * * */

    private const val TAG = "BleManager"

    private val deviceBoundStateReceiver = DeviceBoundBroadcastReceiver()

    /* * * * * * * * * * * * * * * * * * * 可空私有属性 * * * * * * * * * * * * * * * * * * */

    /**
     * 蓝牙管理器
     */
    private var bluetoothManager: BluetoothManager? = null

    /**
     * 蓝牙扫描器单例
     */
    private var bleScannerInstance: BleScanner? = null

    /**
     * 蓝牙连接器单例
     */
    private var bleConnectorInstance: BleConnector? = null

    /* * * * * * * * * * * * * * * * * * * 变量私有属性 * * * * * * * * * * * * * * * * * * */

    private var deviceBoundStateReceiverRegistered = false

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * 内部属性
     *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    /**
     * 蓝牙适配器
     */
    internal var bluetoothAdapter: BluetoothAdapter? = null

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * 内部方法
     *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    /**
     * 初始化
     */
    internal fun init(bleInitProvider: BleInitProvider) {
        val context = bleInitProvider.context
        if (context == null) {
            Logger.log(TAG, "init interrupt: context is null")
            return
        }
        InternalConstants.context = context.applicationContext
        bluetoothManager =
            InternalConstants.context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
        bluetoothAdapter = bluetoothManager?.adapter
        if (bluetoothManager == null) {
            Logger.log(TAG, "get \"android.bluetooth.BluetoothManager\" is null")
        }
        if (bluetoothAdapter == null) {
            Logger.log(TAG, "get \"android.bluetooth.BluetoothAdapter\" is null")
        }
        registerDeviceBoundStateReceiver()
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * 公开方法
     *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    /**
     * 开启Log打印
     * @param enable true:开启，false:关闭
     */
    fun enableLog(enable: Boolean) {
        Logger.enableLog(enable)
    }

    /**
     * 设置log打印级别
     * @param logLevel 日志级别
     */
    fun setLogLevel(logLevel: LogLevel) {
        Logger.setLogLevel(logLevel)
    }

    /**
     * 判断设备是否LE CODED
     */
    @RequiresApi(Build.VERSION_CODES.O)
    fun isLeCodedPhySupported(): Boolean {
        return bluetoothAdapter?.isLeCodedPhySupported ?: false
    }

    /**
     * 判断设备是否支持蓝牙功能
     */
    fun supportBluetooth(): Boolean {
        return bluetoothManager != null
    }

    /**
     * 判断设备是否支持蓝牙功能
     */
    fun supportBluetoothLe(): Boolean {
        return InternalConstants.context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
    }

    /**
     * 判断蓝牙是否开启
     */
    fun isBlueEnable(): Boolean {
        return bluetoothAdapter?.isEnabled == true
    }

    /**
     * 开启蓝牙
     */
    fun enableBluetooth() {
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        InternalConstants.context.startActivity(intent)
    }

    /**
     * 获取蓝牙适配器
     */
    fun getBluetoothAdapter(): BluetoothAdapter? {
        return bluetoothAdapter
    }

    /**
     * 判断某个BLE设备是否已经连接
     */
    @SuppressLint("MissingPermission")
    fun isDeviceConnected(device: BluetoothDevice): Boolean {
        return bluetoothManager?.getConnectedDevices(BluetoothProfile.GATT)
            ?.contains(device) == true
    }

    /**
     * 判断某个BLE设备是否已经连接
     */
    fun isDeviceConnected(address: String): Boolean {
        return isDeviceConnected(bluetoothAdapter?.getRemoteDevice(address) ?: return false)
    }

    /**
     * 获取蓝牙扫描器单例
     * @return 蓝牙扫描器单例
     */
    @Synchronized
    fun getScannerInstance(): BleScanner {
        if (bleScannerInstance == null) {
            bleScannerInstance = BleScanner()
        }
        return bleScannerInstance!!
    }

    /**
     * 释放BLE扫描器单例的内存
     */
    fun releaseBleScannerInstance() {
        bleScannerInstance?.close()
        bleScannerInstance = null
    }

    /**
     *  获取BLE连接器单例
     */
    @Synchronized
    fun getConnectorInstance(): BleConnector {
        if (bleConnectorInstance == null) {
            bleConnectorInstance = BleConnector()
        }
        return bleConnectorInstance!!
    }

    /**
     *  关闭当前连接过的全部设备的GATT连接
     */
    fun closeAllConnectedGatt() {
        bleConnectorInstance?.closeAllGatt()
    }

    /**
     * 释放全部与BLE连接器相关的回调与参数
     */
    fun releaseConnectorInstance() {
        closeAllConnectedGatt()
        GlobalConnectVariable.bluetoothGattConnectStateChangedListenerList.clear()
        GlobalConnectVariable.connectTimeOutTimerList.clear()
        GlobalConnectVariable.connectedStateList.clear()
        GlobalConnectVariable.onDescriptorDataOptionListenerList.clear()
        GlobalConnectVariable.onCharacteristicDataOptionsListenerList.clear()
        GlobalConnectVariable.writeLargeDataKeepList.clear()
        GlobalConnectVariable.writeLargeDataContinueFlagList.clear()
        GlobalConnectVariable.onReadRemoteRssiListenerList.clear()
        GlobalConnectVariable.onMtuChangedListenerList.clear()
        GlobalConnectVariable.onPhyOptionsListenerList.clear()
    }

    /**
     * 判断某个特征是否有对应的属性
     *  @param characteristic BluetoothGattCharacteristic
     *  @param properties 属性
     *  [BluetoothGattCharacteristic.PROPERTY_READ]
     *  [BluetoothGattCharacteristic.PROPERTY_NOTIFY]
     *  [BluetoothGattCharacteristic.PROPERTY_WRITE]
     *  [BluetoothGattCharacteristic.PROPERTY_BROADCAST]
     *  [BluetoothGattCharacteristic.PROPERTY_INDICATE]
     *  [BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS]
     *  [BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE]
     *  [BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE]
     *  @return 是否有对应的属性
     */
    fun checkCharacteristicProperties(
        characteristic: BluetoothGattCharacteristic, properties: Int
    ): Boolean {
        return characteristic.properties and properties == properties
    }

    /**
     * 判断特征是否可读
     */
    fun characteristicCanRead(characteristic: BluetoothGattCharacteristic): Boolean {
        return checkCharacteristicProperties(
            characteristic, BluetoothGattCharacteristic.PROPERTY_READ
        )
    }

    /**
     * 判断特征是否可写
     */
    fun characteristicCanWrite(characteristic: BluetoothGattCharacteristic): Boolean {
        return checkCharacteristicProperties(
            characteristic, BluetoothGattCharacteristic.PROPERTY_WRITE
        )
    }

    /**
     * 判断特征是否支持通知
     */
    fun characteristicCanNotify(characteristic: BluetoothGattCharacteristic): Boolean {
        return checkCharacteristicProperties(
            characteristic, BluetoothGattCharacteristic.PROPERTY_NOTIFY
        )
    }

    /**
     * 判断某个描述是否有对应的权限
     * @param descriptor BluetoothGattDescriptor
     * @param permissions 要判断的权限
     *
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_ENCRYPTED]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_READ_ENCRYPTED_MITM]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED_MITM]
     * [android.bluetooth.BluetoothGattDescriptor.PERMISSION_WRITE_SIGNED_MITM]
     *
     * @return 是否有权限
     */
    fun checkDescriptorPermission(
        descriptor: BluetoothGattDescriptor,
        permissions: Int
    ): Boolean {
        val result = descriptor.permissions and permissions == permissions
        return result
    }

    /**
     * 判断描述是否可读
     */
    fun descriptorCanRead(characteristic: BluetoothGattDescriptor): Boolean {
        return checkDescriptorPermission(
            characteristic, BluetoothGattDescriptor.PERMISSION_READ
        )
    }

    /**
     * 判断描述是否可写
     */
    fun descriptorCanWrite(characteristic: BluetoothGattDescriptor): Boolean {
        return checkDescriptorPermission(
            characteristic, BluetoothGattDescriptor.PERMISSION_WRITE
        )
    }

    /**
     * 绑定设备
     */
    fun bindDevice(device: BluetoothDevice): Int {
        if (!hasBluetoothConnectPermission()) {
            return DeviceBoundErrorCode.PERMISSION_BLUETOOTH_CONNECT_NOT_GRANTED
        }
        when (device.bondState) {
            BluetoothDevice.BOND_BONDED -> return DeviceBoundErrorCode.DEVICE_BOND_BONDED
            BluetoothDevice.BOND_BONDING -> return DeviceBoundErrorCode.DEVICE_BOND_BONDING
            else -> {
                Logger.log(
                    TAG,
                    "设备当前未绑定 发起绑定"
                )
            }
        }
        //使用系统API绑定设备
        return if (device.createBond()) {
            DeviceBoundErrorCode.DEVICE_BOND_REQUEST_SUCCESS
        } else {
            DeviceBoundErrorCode.DEVICE_BOND_REQUEST_FAILED
        }
    }

    /**
     * 添加蓝牙状态变化回调监听
     *
     * @param onBluetoothStateChangedListener 蓝牙状态变化回调监听
     */
    @Suppress("MemberVisibilityCanBePrivate")
    fun setOnBluetoothStateChangedListener(onBluetoothStateChangedListener: OnBluetoothStateChangedListener?) {
        GlobalVariable.onBluetoothStateChangedListener = onBluetoothStateChangedListener
    }

    /**
     * 添加设备绑定状态变化回调监听
     */
    fun addDeviceBoundStateChangedListener(
        address: String,
        listener: OnDeviceBondStateChangedListener
    ) {
        GlobalVariable.onDeviceBondStateChangedListeners[address] = listener
    }

    /**
     * 添加设备绑定状态变化回调监听
     */
    fun addDeviceBoundStateChangedListener(
        device: BluetoothDevice,
        listener: OnDeviceBondStateChangedListener
    ) {
        addDeviceBoundStateChangedListener(device.address, listener)
    }

    /**
     * 移除设备绑定状态变化回调监听
     */
    fun removeDeviceBoundStateChangedListener(address: String): OnDeviceBondStateChangedListener? {
        return GlobalVariable.onDeviceBondStateChangedListeners.remove(address)
    }

    /**
     * 移除设备绑定状态变化回调监听
     */
    fun removeDeviceBoundStateChangedListener(device: BluetoothDevice): OnDeviceBondStateChangedListener? {
        return removeDeviceBoundStateChangedListener(device.address)
    }

    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     *
     * 私有方法
     *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

    /**
     * 注册设备绑定状态的广播接收者
     */
    @SuppressLint("UnspecifiedRegisterReceiverFlag")
    @Synchronized
    private fun registerDeviceBoundStateReceiver() {
        if (deviceBoundStateReceiverRegistered) {
            return
        }
        deviceBoundStateReceiverRegistered = true
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            InternalConstants.context.registerReceiver(
                deviceBoundStateReceiver,
                DeviceBoundBroadcastReceiver.getIntentFilter(), Context.RECEIVER_EXPORTED
            )
        } else {
            InternalConstants.context.registerReceiver(
                deviceBoundStateReceiver,
                DeviceBoundBroadcastReceiver.getIntentFilter()
            )
        }
    }
}
